// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React, { useCallback, useRef } from 'react';

import { mount, unmount } from '~mount';

import styles from './iframe-wrapper.scss';

function copyStyles(srcDoc: Document, targetDoc: Document) {
  for (const stylesheet of Array.from(srcDoc.querySelectorAll('link[rel=stylesheet]'))) {
    const newStylesheet = targetDoc.createElement('link');
    for (const attr of stylesheet.getAttributeNames()) {
      newStylesheet.setAttribute(attr, stylesheet.getAttribute(attr)!);
    }
    targetDoc.head.appendChild(newStylesheet);
  }
}

function syncClasses(from: HTMLElement, to: HTMLElement) {
  to.className = from.className;
  const observer = new MutationObserver(() => {
    to.className = from.className;
  });

  observer.observe(from, { attributes: true, attributeFilter: ['class'] });

  return () => {
    observer.disconnect();
  };
}

export function IframeWrapper({ id, AppComponent }: { id: string; AppComponent: React.ComponentType }) {
  const cleanupRef = useRef<(() => void) | null>(null);

  // use callback ref instead of useEffect to avoid double effect issues in React 18+ strict mode
  const mountIframe = useCallback(
    (container: HTMLElement | null) => {
      if (!container) {
        cleanupRef.current?.();
        cleanupRef.current = null;
        return;
      }
      const iframeEl = container.ownerDocument.createElement('iframe');
      iframeEl.className = styles['full-screen'];
      iframeEl.id = id;
      iframeEl.title = id;
      container.appendChild(iframeEl);

      const iframeDocument = iframeEl.contentDocument!;
      // Prevent iframe document instance from reload
      // https://bugzilla.mozilla.org/show_bug.cgi?id=543435
      iframeDocument.open();
      // set html5 doctype
      iframeDocument.writeln('<!DOCTYPE html>');
      iframeDocument.close();

      const innerAppRoot = iframeDocument.createElement('div');
      iframeDocument.body.appendChild(innerAppRoot);
      copyStyles(document, iframeDocument);
      iframeDocument.dir = document.dir;
      const syncClassesCleanup = syncClasses(document.body, iframeDocument.body);
      mount(<AppComponent />, innerAppRoot);
      cleanupRef.current = () => {
        syncClassesCleanup();
        unmount(innerAppRoot);
        container.removeChild(iframeEl);
      };
    },
    [AppComponent, id]
  );

  return <div ref={mountIframe}></div>;
}
